#define PrynInternal
#define PrynImplementation
#include <pryn.h>

static const PrynPinList nullPinList = {0, 0};

#define PrynComponentBody(BODY) PrynBaseBody (component, { state = component->pState; BODY })
#define PrynComponentGetProperty(PropName, PropType, PropInitial, PropValue) PrynBaseGetProperty (, Component, component, PropName, PropType, PropInitial, { *output = PropValue; })
#define PrynComponentSetProperty(PropName, PropType, PropAssign) PrynBaseSetProperty (, Component, component, PropName, PropType, PropAssign)

PrynComponentGetProperty (State, PrynState *, 0, state)
PrynComponentGetProperty (Tags, PrynTagList *, 0, &component->pCommonObject.pTags)
PrynComponentGetProperty (InputPins, PrynPinList *, 0, &component->pInputPins)
PrynComponentGetProperty (OutputPins, PrynPinList *, 0, &component->pOutputPins)
PrynComponentGetProperty (Factory, PrynFactory *, 0, component->pFactory)
PrynComponentGetProperty (Name, PrynString, nullString, component->pCommonResource.pName)
PrynComponentGetProperty (Description, PrynString, nullString, component->pCommonResource.pDescription)
PrynComponentGetProperty (Proprietary, void *, 0, component->pProprietary)
PrynComponentGetProperty (Monitors, PrynMonitorList *, 0, &component->pCommonObject.pMonitors)
PrynBaseGetProperty (, Component, component, FirstOutputPin, PrynPin *, 0, { *output = component->pOutputPins.first; prynAssert (*output, False); })

PrynExport (PrynResult, prynComponentCreate, (PrynComponent **output, PrynFactory *factory))
{
	PrynStartFunction ();
	PrynComponent *component;

	if (output) *output = 0;
	prynAssert (factory, NullArgument);
	state = factory->pLibrary->pState;
	prynCall (prynFactoryLoad (factory));
	prynAssert (factory->pCreateFunction, Failure);
	prynCall (factory->pCreateFunction (factory, &component, factory->pCreateFunctionData));
	if (output) *output = component;
	PrynEndFunction ();
}

PrynExport (PrynResult, prynComponentAllocate, (PrynComponent **output, PrynFactory *factory, size_t proprietarySize))
{
	PrynStartFunction ();
	PrynComponent *component = 0;

	if (output) *output = 0;
	prynAssert (factory, NullArgument);
	state = factory->pLibrary->pState;
	
	prynCall (prynAllocateCleared (state, sizeof (PrynComponent) + proprietarySize, (void **) &component));
	component->pCommonObject.pType = PrynObjectTypeComponent;
	component->pFactory = factory;
	component->pState = factory->pLibrary->pState;
	component->pCommonResource.pName = factory->pCommonResource.pName;
	component->pCommonResource.pDescription = factory->pCommonResource.pDescription;
	if (proprietarySize)
		component->pProprietary = component + 1;
	prynDListAppendP (factory->pComponents, component);

	if (output) *output = component;
	PrynEndFunction ();
}

PrynExport (PrynResult, prynComponentDisplayName, (PrynComponent *component, PrynString *value))
{
	PrynString nullString = {0, 0};

	if (value) *value = nullString;
	PrynComponentBody ({
		PrynString interim;

		prynAssert (component->pFactory, BadArgument);
		prynCall (component->pFactory->pDisplayNameFunction (component, &interim, component->pFactory->pDisplayNameFunctionData));
		if (value) *value = interim;
	});
}

PrynExport (PrynResult, prynComponentClick, (PrynComponent *component))
{
	PrynStartFunction ();
	PrynFactory *factory;

	prynAssert (component, NullArgument);
	prynAssert (component->pFactory, BadArgument);
	factory = component->pFactory;
	prynReturnCall (factory->pClickFunction (component, factory->pClickFunctionData));
	PrynEndFunction ();
}

PrynExport (PrynResult, prynComponentDestroy, (PrynComponent *component))
{
	PrynComponentBody ({
		PrynFactory *factory = component->pFactory;

		while (component->pInputPins.first)
			prynPinDestroy (component->pInputPins.first);
		while (component->pOutputPins.first)
			prynPinDestroy (component->pOutputPins.first);
		if (factory)
			prynDListRemoveP (factory->pComponents, component);
		prynFree (state, component);
	});
}

PrynExport (PrynResult, prynComponentDisassociate, (PrynComponent *component))
{
	PrynStartFunction ();
	PrynFactory *factory;

	prynAssert (component, NullArgument);
	state = component->pState;
	factory = component->pFactory;
	if (!factory) prynReturnDone ();

	while (component->pInputPins.first)
		prynPinDestroy (component->pInputPins.first);
	while (component->pOutputPins.first)
		prynPinDestroy (component->pOutputPins.first);
	prynDListRemoveP (factory->pComponents, component);
	component->pFactory = 0;

	PrynEndFunction ();
}

static PrynResult IterateDirection (PrynComponent *component, PrynPinDirection direction, PrynPin **pin)
{
	PrynState *state = 0;

	if (component) state = component->pState;
	if (!component || !pin)
	{
		if (pin) *pin = 0;
		return PrynResultNullArgument;
	}

	if (*pin)
	{
		if ((*pin)->pComponent != component || (*pin)->pDirection != direction)
			return PrynResultBadArgument;
		(*pin) = (*pin)->pNext;
	}
	else if (direction == PrynPinDirectionOutput)
		*pin = component->pOutputPins.first;
	else
		*pin = component->pInputPins.first;

	if (*pin)
		return PrynResultSuccess;
	return PrynResultDone;
}

PrynExport (PrynResult, prynComponentIterateOutputPins, (PrynComponent *component, PrynPin **pin))
{
	return IterateDirection (component, PrynPinDirectionOutput, pin);
}

PrynExport (PrynResult, prynComponentIterateInputPins, (PrynComponent *component, PrynPin **pin))
{
	return IterateDirection (component, PrynPinDirectionInput, pin);
}

